#include "zen_image.h"
#include "zen_random.h"
#include "zen_file.h"
#include "zen_image_coder.h"
#include "zen_image_filter.h"
#include "zen_image_stb.h"
#include "zen_number.h"
#include "zen_endian.h"
#include "zen_bezier.h"
#include "zen_image_stb.h"
#include <iostream>
#include <vector>
#include <random>

Zen::Random R;
Zen::Point3 RandPoint()
{
	return { R.NextF<float>(), R.NextF<float>(), 0.f };
}

class Bezier2Img
{
public:
	Bezier2Img(int w, int h)
	{
		mImg = Zen::Image::GenerateBlank(Zen::EPixel::RGB, w, h, 0);

		bezier.SetControl(0, { 0, 0, 0 });
		bezier.SetControl(1, { 0.8, 0.2, 0 });
		bezier.SetControl(2, { 0.2, 0.8, 0 });
		bezier.SetControl(3, { 1, 1, 0 });
		color = Zen::Color3b(123, 230, 200);
	}

	void Render()
	{
		//color.SetRGB((R.Next() & 0xffffff) | (0x606060));
		for (int i = 0; i < 4; ++i)
		{
			bezier.SetControl(i, RandPoint());
		}
		auto p0 = GetPoint(0);
		auto p1 = GetPoint(1);
		Render(0, 1, p0, p1, 0);
	}
	void Render(float a, float b, Zen::Point2 pa, Zen::Point2 pb, int depth)
	{
		using namespace Zen::Number;

		float m = (a + b) / 2.f;
		auto pm = GetPoint(m);
		bool r1 = IsEq(pa, pm);
		bool r2 = IsEq(pm, pb);
		if (!r1 && !r2)
		{
			Render(a, m, pa, pm, depth + 1);
			Render(m, b, pm, pb, depth + 1);
			RenderPoint(pm);
		}
	}
	bool RenderPoint(Zen::Point2 p)
	{
		int sz = 3;
		int x = (int)p.x;
		int y = (int)p.y;
		if (x >= 0 && x + sz <= mImg->Width() && y >= 0 && y + sz <= mImg->Height())
		{
			for (int i = 0; i < sz; ++i)
				for (int j = 0; j < sz; ++j) mImg->SetColor(x + i, y + j, color);
			return true;
		}
		return false;
	}
	Zen::Point2 GetPoint(float i)
	{
		auto p = bezier.GetPoint(i);
		p.x *= mImg->Width();
		p.y *= mImg->Height();
		return { p.x, p.y };
	}
	bool IsEq(Zen::Point2 const& p0, Zen::Point2 const& p1)
	{
		using namespace Zen::Number;
		return (int)p0.x == (int)p1.x && (int)p0.y == (int)p1.y && IsFuzzyEqual(p0.x, p1.x, 0.5f) && IsFuzzyEqual(p0.y, p1.y, 0.5f);
	}
	void WriteToFile(std::string name)
	{
		Zen::ImageJPGEncoder jpeg(1);
		auto data = jpeg.encode(*mImg.get());
		Zen::File::WriteBufferToFile(name, data);
	}

	Zen::Bezier3Dx4 bezier;
	Zen::Color3b color;

private:
	std::shared_ptr<Zen::Image> mImg;
};

int main()
{
	for (int n = 0; n < 10; ++n)
	{
		R.Reset(::clock() + time(0));
		Bezier2Img b2i(3000, 3000);
		for (int i = 0; i < 80; ++i)
		{
			b2i.Render();
		}
		b2i.WriteToFile(std::to_string(n) + ".jpg");
	}
	return 0;
}
